#pragma once
#include<jsoncpp/json/json.h>
#include<unistd.h>
#include<signal.h>
#include"compile.hpp"
#include"run.hpp"
#include"../comm/util.hpp"
#include"../comm/log.hpp"


namespace ns_compile_and_run
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_compiler;
    using namespace ns_runner;
    class CompileAndRun
    {
        public:
        //code>0 :进程收到了信号导致异常奔溃
        //code<0 :整个过程非运行报错(代码为空，编译报错)
        //code=0 :整个过程运行完成
        static std::string CodeToDst(int code,const std::string& file_name)
        {
            std::string desc;
          switch(code)
          {
              case 0:
              desc="编译运行成功";
              break;
              case -1:
              desc="提交的代码是空的";
                break;
              case -2:
              desc="未知错误";
                break;
              case -3:
              FileUtil::ReadFile(PathUtil::CompileError(file_name),&desc,true);
                break;
              case 6:
              desc="内存超过范围";
              break;
              case 8:
              desc="浮点数溢出错误";
              break;
              case 24:
              desc="CPU使用超时";
              break;
             default:
             desc="未知:"+std::to_string(code);
             break;
          }
          return desc;
        }
        CompileAndRun()
        {}
        ~CompileAndRun()
        {}

        //清理临时文件
       static void RemoveTemp(const std::string& file_name)
       {
          std::string _src=PathUtil::Src(file_name);
          if(FileUtil::IsFileExists(_src))
          unlink(_src.c_str());
          std::string _compileerror=PathUtil::CompileError(file_name);
          if(FileUtil::IsFileExists(_compileerror))
          unlink(_compileerror.c_str());
          std::string _execute=PathUtil::Exe(file_name);
          if(FileUtil::IsFileExists(_execute))
          unlink(_execute.c_str());
          std::string _stdin=PathUtil::Stdin(file_name);
          if(FileUtil::IsFileExists(_stdin))
          unlink(_stdin.c_str());
          std::string _stdout=PathUtil::Stdout(file_name);
          if(FileUtil::IsFileExists(_stdout))
          unlink(_stdout.c_str());
          std::string _stderr=PathUtil::Stderr(file_name);
          if(FileUtil::IsFileExists(_stderr))
          unlink(_stderr.c_str()); 
       }
       /**********************************************
       *输入:
       * code:用户提交的代码
       * input:用户给自己提交的代码对应的输入，不做处理
       * cpu_limit
       * mem_limit
       *
       *输出:
       * 必填
       *  status:状态码
       *  reason:状态码的描述
       * 选填,编译报错，下面两个参数没有
       *  stdout:我的程序运行完的结果
       *  stderr:我的程序运行完错误的结果
       *in_json:{"code":"#inclde";"input":"";"cpu_limit","mem_limit"}
       *out_json{"status","season","stdout","stderr"};
       ************************************************/
        static void Start(const std::string& in_json,std::string* out_json)
        {
           //1.先反序列化传进来的json串
            Json::Value in_value;
            Json::Reader reader;
            reader.parse(in_json,in_value);

            std::string code=in_value["code"].asString();
            std::string input=in_value["input"].asString();
            int cpu_limit=in_value["cpu_limit"].asInt();
            int mem_limit=in_value["mem_limit"].asInt();
            int status_code=0;//状态码
            int _signal=0;
            std::string file_name;//因为goto END中间是不能有定义的；
            if(code.size()==0)
            {
                //说明客户没有提交上来代码
                // Json::Value out_value;
                // out_value["status"]=-1;
                // out_value["reason"]="当前用户提交的代码为空"
                status_code=-1;
                goto END;
            }
            
           //2.将用户提交的代码写入到temp文件中，但是可能存在很多用户同时提交代码
           //的可能，所以我们要设置一个函数，确保用户写入到的文件是唯一的；
           
           //通过毫秒级时间戳+原子性递增唯一值:确保唯一性
           file_name=FileUtil::UniqueFileName(); //该函数定义在util.hpp中
           
           //3.把内容写入到文件中，形成临时Src文件
           //上面的函数形成的文件名，只是个文件名，没有目录，没有后缀
           if(!FileUtil::WriteFile(PathUtil::Src(file_name),code))
           {
                status_code=-2;//未知错误，服务器内部的错误，和用户代码无关，不暴露给用户
                goto END;
           }
           
           //4.进行编译并且运行
           if(!Compiler::Compile(file_name))
           {
               //编译失败
                status_code=-3;
                goto END;
           }

           _signal=(Runner::Run(file_name,cpu_limit,mem_limit));
           if(_signal<0)
           {   
               //实际上的错误是服务器的内部错误，可能是open、fork失败
               //但是服务器错误和代码无关，所以服务器错误不能暴露给用户
                status_code=-2;
           }
           else if(_signal>0)
           {
                status_code=_signal;//运行报错，
           }
           else
           {
            status_code=0;
           }
           END:
            //进行序列化
            Json::Value out_value;
            out_value["status"]=status_code;
            out_value["reason"]=CodeToDst(status_code,file_name);
            if(status_code==0)
            {
               std::string _stdout;
               FileUtil::ReadFile(PathUtil::Stdout(file_name),&_stdout,true);
               out_value["stdout"]=_stdout;
               std::string _stderr;
               FileUtil::ReadFile(PathUtil::Stderr(file_name),&_stderr,true);
               out_value["stderr"]=_stderr;
            }
          Json::StyledWriter writer;
          *out_json=writer.write(out_value);
          RemoveTemp(file_name);
        }
    };
}